#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include "listlib.h"

#define TRAV_INIT_SIZE   8
#define FILE_MODE (O_CREAT | O_RDWR | O_APPEND)

typedef struct list_struct{
	data_t item;
	struct list_struct *next;
}list_t;

static list_t endlist;
static list_t *headptr = NULL;
static list_t *tailptr = NULL;
static list_t **travptrs = NULL;
static int travptrs_size = 0;

// init the data
int init_data(data_t *data,time_t time,const char *str)
{
	if (data == NULL){
		errno = EINVAL;
		return -1;
	}
	data->time = time;
	if (data->string){
		free_data(data);
	}
	data->string = (char *)malloc(strlen(str) + 1);
	if (data->string == NULL){
		return -1;
	}

	strcpy(data->string,str);
	return 0;
}

int free_data(data_t *data)
{
	if (data == NULL){
		errno = EINVAL;
		return -1;
	}

	if (data->string){
		free(data->string);
		data->string = NULL;
	}

	return 0;
}

void show_data(data_t data)
{
	char *cp;

	cp = ctime(&data.time);
	printf("%s%s\n",cp,data.string);
}

//return a nonnegative key if successful
int accessdata(void)
{
	int i;
	list_t **newptrs;

	if(headptr == NULL || tailptr == NULL){
		errno = EINVAL;
		return -1;
	}

	if (travptrs_size == 0){
		travptrs = (list_t **) calloc(TRAV_INIT_SIZE,sizeof(list_t *));
		if (travptrs == NULL){
			return -1;
		}
		travptrs[0] = headptr;
		travptrs_size = TRAV_INIT_SIZE;
		return 0;
	}
	
	// look for an empty slot for key.
	for (i = 0;i < travptrs_size;i++){
		if (travptrs[i] == NULL){
			travptrs[i] = tailptr;
			return i;
		}
	}

	newptrs = realloc(travptrs,2*travptrs_size*sizeof(list_t *));
	if (newptrs == NULL){
		return -1;
	}
	travptrs = newptrs;
	travptrs[travptrs_size] = tailptr;
	travptrs_size *= 2;

	return travptrs_size/2;
}

int adddata(data_t data)
{
	list_t *newnode;
	int nodesize;

	nodesize = sizeof(list_t) + strlen(data.string) + 1;
	newnode = malloc(nodesize);
	if (newnode == NULL){
		return -1;
	}

	newnode->item.time = data.time;
	// locate the string address
	newnode->item.string = (char *) newnode + sizeof(list_t);
	strcpy(newnode->item.string,data.string);
	newnode->next = NULL;

	if (headptr == NULL){
		headptr = newnode;
	}
	else {
		tailptr = newnode;
		if (headptr->next == NULL){
			headptr->next = tailptr;
		}
	}
	tailptr = newnode;

	return 0;
}

// copy next item and set datap->string
int getdata(int key,data_t *datap)
{
	
	list_t *t = NULL;

	if (key < 0 || key > travptrs_size || travptrs[key] == NULL  ){
		errno = EINVAL;
		perror("key");
		return -1;
	}

	// end of list,set datap->string to NULL
	if (travptrs[key] == &endlist){
		datap->string = NULL;
		travptrs[key] = NULL;
		return 0;
	}
	
	t = travptrs[key];
	datap->string = (char *)malloc(strlen(t->item.string) + 1);
	if (datap->string == NULL){
		return -1;
	}

	datap->time = t->item.time;
	strcpy(datap->string,t->item.string);
	if (t->next == NULL){
		travptrs[key] = &endlist;
	}
	else{
		travptrs[key] = t->next;
	}
	
	return 0;
}

int freekey(int key)
{
	if (key < 0 || key > travptrs_size){
		errno = EINVAL;
		return -1;
	}

	travptrs[key] = NULL;
	return 0;
}

int length(void)
{
	list_t *t;
	int len = 0;

	t = headptr;

	for (; t; t = t->next){
		len++;
	}

	return len;
}

int save_data(const char *filename)
{
	int fd;

	fd = open(filename,FILE_MODE, S_IWUSR);
	if (fd < 0){
		perror(filename);
		return -1;
	}

	return 0;

}

